﻿using System.Windows;
using snat.model;
using System.ComponentModel;
using System;
using MySql.Data.MySqlClient;

namespace gui {

    /// <summary>
    /// Interaction logic for PlayControl.xaml 
    /// Play Control UI element that appears on each tab
    /// </summary>
    public sealed partial class PlayControl : System.Windows.Controls.UserControl {

        public static event guiFreezeGraphLayoutEvent FreezeGraphEvent;
        public static event pcRunning RunningEvent;
        public static event pcSkipToEnd SkipToEndEvent;
        public static event pcReady ReadyEvent;
        public static event pcPaused PausedEvent;
        public static event pcFinished FinishedEvent;
        public static event pcStepChanged StepChangedEvent;
        public static event pcStepTypeChanged StepTypeChangedEvent;
        public static event guiOnFinishedProcessingEvent OnFinishedProcessingEvent;

        // Number of messages elapsed since a play/step command was called, or current datetime
        private static int messagesProcessed;
        private static DateTime timeProcessed;
        private static bool registeredEvents = false;

        // ObjectLock
        private static object _objLock = new object();

        public PlayControl() {
            // Set up data binding
            PlayControlConnect._Dispatcher = this.Dispatcher;
            this.DataContext = PlayControlConnect.Instance;
            InitializeComponent();

            // Register NetworkModel progress events (only once instead of per tab)
            lock (_objLock) {
                if (!registeredEvents) {
                    registeredEvents = true;
                    NetworkModel.Instance.NodeAdded += guiOnNodeAdded;
                    NetworkModel.Instance.EdgeAdded += guiOnEdgeAdded;
                    NetworkModel.Instance.MessageProcessed += guiOnMessageProcessed;
                    NetworkModel.Instance.TapeLoaded += guiOnInitialised;
                    Menu.OnResetEvent += guiOnInitialising;
                }
            }

            // Register play control events
            NetworkModelConnect.TapeLoadedEvent += Ready;
            RunningEvent += Running;
            SkipToEndEvent += DisableButtons;
            ReadyEvent += Ready;
            PausedEvent += Paused;
            FinishedEvent += DisableButtons;
            FinishedEvent += SetRunProgressTo100;
            StepChangedEvent += SetStepValue;
            StepTypeChangedEvent += SetStepTypeSelection;
        }

        #region ********** PLAY CONTROL ALGORITHM SPAWNER METHODS **********

        private static void WorkerPlayAlgorithm(object sender, DoWorkEventArgs e) {
            StatusInfo.SetStatus("Communicating with Database");
            ConsolePane.WriteLine("Playing through all messages.");
            try {
                NetworkModel.Instance.Play();
            } catch (MySqlException ex) {
                MessageBox.Show("The dataset cannot be played through because an error has occured!\n\nMySQL Exception: " + ex.Message, "Dataset Play Through Halted", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void PlayAlgorithm(object sender, RoutedEventArgs e) {
            if (NetworkModel.Instance.TapeIsPaused) {
                if (RunningEvent != null) RunningEvent();
                messagesProcessed = NetworkModel.Instance.MessageProcessedCount;
                timeProcessed = NetworkModel.Instance.CurrentTime;
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += WorkerPlayAlgorithm;
                worker.RunWorkerAsync();
                worker.RunWorkerCompleted += ProcessGraph;
            }
        }

        private static void WorkerSkipToEnd(object sender, DoWorkEventArgs e) {
            StatusInfo.SetStatus("Communicating with the Database");
            ConsolePane.WriteLine("Skipping to End.");
            try {
                NetworkModel.Instance.SkipAllMessages();
            } catch (MySqlException ex) {
                MessageBox.Show("The dataset cannot be skipped through because an error has occured!\n\nMySQL Exception: " + ex.Message, "Dataset Play Through Halted", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void SkipToEnd(object sender, RoutedEventArgs e) {
            if (NetworkModel.Instance.TapeIsPaused) {
                if (RunningEvent != null) RunningEvent();
                if (SkipToEndEvent != null) SkipToEndEvent();

                // Create new loading box
                LoadingBox lb = new LoadingBox("Skipping through all messages.");
                lb.Show();

                // Run up SkipToEnd worker
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += WorkerSkipToEnd;
                worker.RunWorkerAsync();
                worker.RunWorkerCompleted += ProcessGraph;
            }
        }

        private void PauseAlgorithm(object sender, RoutedEventArgs e) {
            if (ReadyEvent != null) ReadyEvent();
            NetworkModel.Instance.Pause();
            ProcessGraph();
        }

        public static int Steps { get; set; }
        public static int StepCatergory { get; set; }
        private static void WorkerStepByAlgorithm(object sender, DoWorkEventArgs e) {
            StatusInfo.SetStatus("Communicating with the Database");
            try {
                TimeSpan timeElapsed = new TimeSpan();
                switch (StepCatergory) {
                    case 1:
                        NetworkModel.Instance.Step(Steps);
                        messagesProcessed = NetworkModel.Instance.MessageProcessedCount - messagesProcessed;
                        ConsolePane.WriteLine(messagesProcessed + " steps/messages have been made.");
                        break;
                    case 2:
                        NetworkModel.Instance.Step(new TimeSpan(Steps, 0, 0, 0));
                        timeElapsed = NetworkModel.Instance.CurrentTime - timeProcessed;
                        ConsolePane.WriteLine((int)timeElapsed.TotalDays + " days stepped through.");
                        break;
                    case 3:
                        NetworkModel.Instance.Step(new TimeSpan(Steps, 0, 0));
                        timeElapsed = NetworkModel.Instance.CurrentTime - timeProcessed;
                        ConsolePane.WriteLine((int)timeElapsed.TotalHours + " hours stepped through.");
                        break;
                    case 4:
                        NetworkModel.Instance.Step(new TimeSpan(0, Steps, 0));
                        timeElapsed = NetworkModel.Instance.CurrentTime - timeProcessed;
                        ConsolePane.WriteLine((int)timeElapsed.TotalMinutes + " minutes stepped through.");
                        break;
                    case 5:
                        NetworkModel.Instance.Step(new TimeSpan(0, 0, Steps));
                        timeElapsed = NetworkModel.Instance.CurrentTime - timeProcessed;
                        ConsolePane.WriteLine((int)timeElapsed.TotalSeconds + " seconds stepped through.");
                        break;
                    default:
                        break;
                }
            } catch (MySqlException ex) {
                MessageBox.Show("The dataset cannot be stepped through because an error has occured!\n\nMySQL Exception: " + ex.Message, "Dataset Play Through Halted", MessageBoxButton.OK, MessageBoxImage.Error);
            }

        }
        private void StepByAlgorithm(object sender, RoutedEventArgs e) {
            if (NetworkModel.Instance.TapeIsPaused) {
                if (RunningEvent != null) RunningEvent();
                messagesProcessed = NetworkModel.Instance.MessageProcessedCount;
                timeProcessed = NetworkModel.Instance.CurrentTime;
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += WorkerStepByAlgorithm;
                worker.RunWorkerAsync();
                worker.RunWorkerCompleted += ProcessGraph;
            }
        }

        #endregion

        #region ********** PROCESS GRAPH METHODS **********

        // Process graph methods used to call graph to re-render
        public void ProcessGraph(object sender, RunWorkerCompletedEventArgs e) {
            if (e.Error != null && e.Cancelled == true) {
                StatusInfo.SetStatus("Error: Unable to process.");
                Console.WriteLine("ERROR: " + e.ToString());
            } else {
                ProcessGraph();
                if (OnFinishedProcessingEvent != null) OnFinishedProcessingEvent();
            }
        }
        private void ProcessGraph() {
            StatusInfo.SetStatus("Calculating new graph layout");
            if (FreezeGraphEvent != null) FreezeGraphEvent();
            NodeStatsInfo.UpdateDisplay();
            NetworkViewModel.ProcessGraph();
            if (NetworkModel.Instance.TapeHasEnded) {
                StatusInfo.SetStatus("Completed");
                ConsolePane.WriteLine(Environment.NewLine + "Dataset has been fully played through.");
                if (FinishedEvent != null) FinishedEvent();
            } else {
                if (ReadyEvent != null) ReadyEvent();
                StatusInfo.SetStatus("Ready");
            }
        }

        #endregion

        #region ********** PLAY CONTROL BUTTONS **********

        // Methods to Enable/Disable Play Control Buttons
        private void Running() {
            Steps = (int)JumpIndex.Value;
            StepCatergory = (StepTypeMenu.SelectedItem as StepType).Catergory;
            PauseButton.IsEnabled = true;
            PlayButton.IsEnabled = false;
            SkipToEndButton.IsEnabled = false;
            StepButton.IsEnabled = false;
        }

        private void Ready() {
            PauseButton.IsEnabled = false;
            PlayButton.IsEnabled = true;
            SkipToEndButton.IsEnabled = true;
            StepButton.IsEnabled = true;
        }

        private void Paused() {
            PauseButton.IsEnabled = false;
            PlayButton.IsEnabled = true;
            SkipToEndButton.IsEnabled = true;
            StepButton.IsEnabled = true;
        }

        private void DisableButtons() {
            PauseButton.IsEnabled = false;
            SkipToEndButton.IsEnabled = false;
            PlayButton.IsEnabled = false;
            StepButton.IsEnabled = false;
        }

        #endregion

        // Re-eventify NetworkModel events
        private void PausedProcessing() {
            if (PausedEvent != null) PausedEvent();
        }
        private void FinishedProcessing() {
            if (FinishedEvent != null) FinishedEvent();
        }

        // Step Value Update Methods - to ensure consistency between different tabs
        private void JumpIndex_ValueChanged(object sender, VIBlend.WPF.Controls.ValueChangedEditorEventArgs args) {
            if (StepChangedEvent != null) StepChangedEvent(args.Value);
        }
        private void SetStepValue(decimal steps) {
            JumpIndex.Value = steps;
        }

        // Step Type Update Methods - to ensure consistency between different tabs
        private void StepTypeMenu_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
            if (StepTypeChangedEvent != null) StepTypeChangedEvent(StepTypeMenu.SelectedIndex);
        }
        private void SetStepTypeSelection(int index) {
            if (StepTypeMenu.SelectedIndex != index) StepTypeMenu.SelectedIndex = index;
        }

        // Call to update the progress bar on new nodes/edges
        private void guiOnNodeAdded(int id, BaseNode b) {
            PlayControlConnect.IncrementNodeCount();
            if (NetworkModel.Instance.TapeHasLoaded) PlayControlConnect.UpdateTimeNow();
            else PlayControlConnect.Initialising();
        }
        private void guiOnEdgeAdded(Edge e) {
            PlayControlConnect.IncrementEdgeCount();
            if (NetworkModel.Instance.TapeHasLoaded) PlayControlConnect.UpdateTimeNow();
            else PlayControlConnect.Initialising();
        }
        private void guiOnMessageProcessed(Message m) {
            PlayControlConnect.IncrementMessageCount();
            if (NetworkModel.Instance.TapeHasLoaded) PlayControlConnect.UpdateTimeNow();
            else PlayControlConnect.Initialising();
        }
        private void guiOnInitialising() {
            PlayControlConnect.Initialising();
        }
        private void guiOnInitialised() {
            PlayControlConnect.Initialised();
        }
        // On Dataset Play Through Completed, set run progress to 100%
        private void SetRunProgressTo100() {
            PlayControlConnect.Instance.ProgressPercentage = "100 % (Completed)";
        }
    }
}
